import * as crypto from "crypto";
import { Obj } from "../config/type";
export class util {
  /**
   * 深层拷贝对象和数组
   * @param sObj 原对象
   * @returns 新对象
   */
  public static clone(sObj: any): any {
    if (sObj === null || typeof sObj !== "object") {
      return sObj;
    }

    let s: any = {};
    if (sObj.constructor === Array) {
      s = [];
    }

    for (const i in sObj) {
      if (sObj[i]) {
        s[i] = this.clone(sObj[i]);
      }
    }

    return s;
  }
  /**
   * 判断传入的参数是否为空的Object。数组或undefined会返回false
   * @param obj
   * @returns Boolean
   */
  public static isEmptyObject(obj: any): boolean {
    let result = true;
    if (obj && obj.constructor === Object) {
      for (const key in obj) {
        if (obj[key]) {
          result = false;
          break;
        }
      }
    } else {
      result = false;
    }
    return result;
  }
  /**
   * 将对象转化为数组格式
   * @param  srcObj 原对象
   * @returns 新数组
   * @example {a:1,b:2} 转换后为 [1,2]
   */
  public static objectToArray(srcObj: object): Array<any> {
    const resultArr: any = [];
    for (const key in srcObj) {
      if (!srcObj[key]) {
        continue;
      }
      resultArr.push(srcObj[key]);
    }

    return resultArr;
  }
  /**
   * 获取对象属性数量
   * @param o 原对象
   * @returns  数量
   */
  public static getPropertyCount(o: object): number {
    let n,
      count = 0;
    for (n in o) {
      if (o[n]) {
        count++;
      }
    }
    return count;
  }
  /**
   * 将数组转化为对象格式
   * @param srcObj 要转换的对象
   * @returns objectKey 转换后的数组
   * @deprecated 待测试
   */
  public static arrayToObject(srcObj: any, objectKey: any) {
    const resultObj: any = {};
    for (const key in srcObj) {
      if (!srcObj[key] || !srcObj[key][objectKey]) {
        continue;
      }
      resultObj[srcObj[key][objectKey]] = srcObj[key];
    }
    return resultObj;
  }
  /**
   * 判断两个值严格意义上是否相等(包含数组和对象)
   * @param x 原对象x
   * @param y 原对象y
   * @returns Boolean
   */
  public static isEqual(x: any, y: any): boolean {
    const in1 = x instanceof Object;
    const in2 = y instanceof Object;
    if (!in1 || !in2) {
      return x === y;
    }
    if (Object.keys(x).length !== Object.keys(y).length) {
      return false;
    }
    for (const p in x) {
      const a = x[p] instanceof Object;
      const b = y[p] instanceof Object;
      if (a && b) {
        return this.isEqual(x[p], y[p]);
      } else if (x[p] !== y[p]) {
        return false;
      }
    }

    return true;
  }
  /**
   * @param collection 统计原对象的所有键值的和
   * @param  predicate 即时回调函数应 返回参数 value
   * @returns Number 和
   * @example {a:2,b:3}=5
   */
  public static getSumBy(collection: { [name: string]: number }, predicate: (value: number) => number): number {
    let sum = 0;
    for (const key in collection) {
      sum += predicate(collection[key]);
    }

    return sum;
  }
  /**
   * @param collection 统计原对象内相同值的数量
   * @returns  Object 新对象
   * @example { a: 2, b: 3, c: 2 }={2:2,3:1}
   */
  public static getCountBy(collection: { [name: string]: number }): object {
    const objRet: { [id: number]: number } = {};
    for (const key in collection) {
      const value = collection[key];
      if (objRet[value]) {
        objRet[value] += 1;
      } else {
        objRet[value] = 1;
      }
    }
    return objRet;
  }
  /**
   * 获取该数组中最小的值
   * @param array 原数组
   *  @returns number
   */
  public static getMin(array: Array<number>): number {
    let result = 0;
    if (array.constructor === Array) {
      const length = array.length;
      for (let i = 0; i < length; i++) {
        if (i === 0) {
          result = Number(array[0]);
        } else {
          result = result > Number(array[i]) ? Number(array[i]) : result;
        }
      }
    }
    return result;
  }
  /**
   * 获取该数组中最大的值
   * @param array 原数组
   * @returns number
   */
  public static getMax(array: any[] | null): number {
    if (array && array.length) {
      var result;
      for (var i = 0; i < array.length; i++) {
        if (i === 0) {
          result = array[0];
        } else if (result < array[i]) {
          result = array[i];
        }
      }

      return result;
    }

    return 0;
  }
  /**
   * 取出数组内最小值（有回调）
   * @param array 原数组
   * @param predicate 回调函数返回值 应返回 value
   * @returns Number 最小值
   */
  public static getMinBy(array: number[] | null, predicate: (value: number) => number): number {
    if (array && array.length) {
      let result = -1;
      let objResult = -1;
      for (var i = 0; i < array.length; i++) {
        if (i === 0) {
          result = predicate(array[0]);
          objResult = array[0];
        } else if (result > array[i]) {
          result = predicate(array[i]);
          objResult = array[i];
        }
      }

      return objResult;
    }

    return 0;
  }
  /**
   * 取出数组内最大值（有回调）
   * @param array 原数组
   * @param predicate 回调函数返回值 应返回 value
   * @returns Number 最大值
   */
  public static getMaxBy(array: number[] | null, predicate: (value: number) => number): number {
    if (array && array.length) {
      let result = -1;
      let objResult = -1;
      for (let i = 0; i < array.length; i++) {
        if (i === 0) {
          result = predicate(array[0]);
          objResult = array[0];
        } else if (result < array[i]) {
          result = array[i];
          objResult = array[i];
        }
      }

      return objResult;
    }

    return 0;
  }
  /**
   * 根据权重随机计算内容
   * @param weightArr  总的权重数组
   * @param totalWeight 获取的总数
   * @returns number 权重值
   */
  public static getWeightRandIndex(weightArr: Array<number>, totalWeight: number): number {
    const randWeight = Math.floor(Math.random() * totalWeight);
    let sum = 0;
    let weightIndex = 0;
    for (weightIndex; weightIndex < weightArr.length; weightIndex++) {
      sum += weightArr[weightIndex];
      if (randWeight < sum) {
        break;
      }
    }
    return weightIndex;
  }
  /**
   * 从0-某个总数中获取m个随机数
   * @param n   最大数限制
   * @param m    位数限制
   * @returns Array 新数组
   * @example getRandomNFromM(10,5)=[4,5,3,7,8]
   */
  public static getRandomNFromM(n: number, m: number): Array<number> {
    const array: number[] = [];
    let intRd = 0;
    let count = 0;

    while (count < m) {
      if (count >= n + 1) {
        break;
      }

      intRd = this.getRandomInt(0, n);
      let flag = 0;
      for (let i = 0; i < count; i++) {
        if (array[i] === intRd) {
          flag = 1;
          break;
        }
      }

      if (flag === 0) {
        array[count] = intRd;
        count++;
      }
    }

    return array;
  }
  /**
   *
   * 获取区间的随机数
   * @param min  最小的数
   * @param max   最大的数
   * @returns Number 获取的随机数
   */
  public static getRandomInt(min: number, max: number): number {
    const r = Math.random();
    const rr = r * (max - min + 1) + min;
    return Math.floor(rr);
  }
  /**
   *
   * 返回随机抽中的奖品
   * @param arr {type：奖品类型。rate:抽中几率 千分比}
   * @returns object  获取抽中的奖品。
   */
  public static getRanLottery(arr: Array<{ type: any; rate: number }>): { type: any; rate: number } {
    let total: any = [];
    arr.forEach((v) => {
      total = total.concat(new Array(v.rate).fill(v));
    });
    const result = this.randShuffle(total);
    const ranNum = this.getRandomInt(0, total.length);
    return result[ranNum];
  }
  /**
   * 将原数组分割为新的二维数组
   * @param array 原数组
   * @param size 分割单位
   * @returns 新数组
   */
  public static chunk(array: any[] | null, size: number): Array<any> {
    const length = array === null ? 0 : array.length;
    if (!length || size < 1) {
      return [];
    }
    const result: any = [];
    while (array!.length > size) {
      result.push(array!.slice(0, size));
      array = array!.slice(size);
    }
    result.push(array);
    return result;
  }
  /**
   * 删除原数组中指定的数组成员并返回新的数组
   * @param array 原数组
   * @param  predicate 检索回调函数(返回参数 true为保留false为删除)
   * @returns 新数组
   */
  public static remove(array: any[], predicate: (value: any) => boolean): Array<any> {
    const result: any[] = [];
    const indexes: number[] = [];
    array.forEach(function (item, index) {
      if (predicate(item)) {
        result.push(item);
        indexes.push(index);
      }
    });

    this.basePullAt(array, indexes);
    return result;
  }
  /**remove函数调用 */
  private static basePullAt(array: any[] | null, indexes: any[]) {
    var length = array ? indexes.length : 0;
    var lastIndex = length - 1;
    var previous;

    while (length--) {
      var index = indexes[length];
      if (length === lastIndex || index !== previous) {
        previous = index;
        Array.prototype.splice.call(array, index, 1);
      }
    }

    return array;
  }
  /**
   * 去掉数组中的重复的数据
   * @param array 原数组
   * @returns  新数组
   */
  public static uniq(array: any[]) {
    const result: any[] = [];
    array.forEach(function (item) {
      if (result.indexOf(item) === -1) {
        result.push(item);
      }
    });
    return result;
  }
  /**
   * 返回一个差异化数组（返回与数组B 不同的新数组）
   * @param  arrayA 原数组
   * @param  diffB 比较的数组
   * @returns 差异化数组
   * @example difference([1,2,3,4],[1,2])=[3,4]
   */
  public static difference(arrayA: Array<any>, diffB: Array<any>): Array<any> {
    const result: number[] = [];
    if (arrayA.constructor !== Array || diffB.constructor !== Array) {
      return result;
    }

    const length = arrayA.length;
    for (let i = 0; i < length; i++) {
      if (diffB.indexOf(arrayA[i]) === -1) {
        result.push(arrayA[i]);
      }
    }

    return result;
  }
  /**
   * 返回一个差异化数组并且回调（返回与数组B 不同的新数组）
   * @param  arrayA 原数组
   * @param diffB 比较的数组
   * @param comparator 数组元素相同时的回调
   * @returns 差异化的数组
   * @example difference([1,2,3,4],[1,2])=[3,4]
   */
  public static diffeAllWith(array: any[], value: any[], comparator: (n: any, item: any) => any): Array<any> {
    value.forEach(function (item) {
      const res = array.filter(function (n) {
        return comparator(n, item);
      });

      res.forEach(function (item) {
        var index = array.indexOf(item);
        if (array.indexOf(item) !== -1) {
          array.splice(index, 1);
        }
      });
    });

    return array;
  }
  /**
   *  对数组进行随机洗牌操作
   * @param arr 需要洗牌的数组
   */
  public static randShuffle(arr: Array<any>) {
    const arrClone = this.clone(arr);
    // 首先从最大的数开始遍历，之后递减
    for (let i = arrClone.length - 1; i >= 0; i--) {
      // 随机索引值randomIndex是从0-arrClone.length中随机抽取的
      const randomIndex = Math.floor(Math.random() * (i + 1));
      // 下面三句相当于把从数组中随机抽取到的值与当前遍历的值互换位置
      const itemIndex = arrClone[randomIndex];
      arrClone[randomIndex] = arrClone[i];
      arrClone[i] = itemIndex;
    }
    // 每一次的遍历都相当于把从数组中随机抽取（不重复）的一个元素放到数组的最后面（索引顺序为：len-1,len-2,len-3......0）
    return arrClone;
  }
  /**********************************************************************
   * 返回当前的时间戳
   * @param type 默认毫秒/true为秒
   * @returns number 时间戳
   */
  public static now(type?: boolean): number {
    return !type ? Date.now() : Math.floor(Date.now() / 1000);
  }
  /**
   * 判断是否是新的一天
   * @param dateValue 旧的时间
   * @returns Boolean
   */
  public static isNewDay(dateValue: Date | string | number): boolean {
    // todo：是否需要判断时区？
    const oldDate = new Date(dateValue);
    const curDate = new Date();

    const oldYear = oldDate.getFullYear();
    const oldMonth = oldDate.getMonth();
    const oldDay = oldDate.getDate();
    const curYear = curDate.getFullYear();
    const curMonth = curDate.getMonth();
    const curDay = curDate.getDate();

    if (curYear > oldYear) {
      return true;
    } else {
      if (curMonth > oldMonth) {
        return true;
      } else {
        if (curDay > oldDay) {
          return true;
        }
      }
    }

    return false;
  }
  /**
   * 判断当前时间是否在有效的时间段内
   * @param start 起始时间。文本时间或时间戳
   * @param end 结束时间。文本时间或时间戳
   * @returns 真或假
   */
  public static isNowValid(start: string | number, end: string | number): boolean {
    const startTime = new Date(start);
    const endTime = new Date(end);
    let result = false;

    if (startTime.getDate() + "" !== "NaN" && endTime.getDate() + "" !== "NaN") {
      const curDate = new Date();
      result = curDate < endTime && curDate > startTime;
    }

    return result;
  }
  /**
   * 获取两个日期相差的天数
   * @param  start 起始时间。参数示例 2014/08/09/时间戳
   * @param  end 结束时间。参数示例 2014/09/20/时间戳
   * @returns  相差的天数
   */
  public static getDeltaDays(start: string | number, end: string | number): number {
    const startData = new Date(start);
    const endData = new Date(end);

    const startYear = startData.getFullYear();
    const startMonth = startData.getMonth() + 1;
    const startDate = startData.getDate();
    const endYear = endData.getFullYear();
    const endMonth = endData.getMonth() + 1;
    const endDate = endData.getDate();

    start = new Date(startYear + "/" + startMonth + "/" + startDate + " GMT+0800").getTime();
    end = new Date(endYear + "/" + endMonth + "/" + endDate + " GMT+0800").getTime();

    const deltaTime = end - start;
    return Math.floor(deltaTime / (24 * 60 * 60 * 1000));
  }
  /**
   * 根据类型 获取指定的格式化日期
   * @param type 1-6
   * @param Date data 是否传入日期 不传则为当前日期
   * @returns 格式化后的日期文本型
   * @example 类型1 2015-06-03 12:15:20 类型2 2015-06-03 类型3 12:15:20 类型4 06-03 12:15:20 类型5 20150603 类型6 20150603121520
   */
  public static formaDate = function (type?: 1 | 2 | 3 | 4 | 5 | 6, data?: Date | string | number): string {
    var dd = data ? new Date(data) : new Date();
    const y: any = dd.getFullYear(); //年
    let m: any = dd.getMonth() + 1; //月
    m = m < 10 ? "0" + m : m;
    let d: any = dd.getDate(); //日
    d = d < 10 ? "0" + d : d;
    let h: any = dd.getHours(); //时
    h = h < 10 ? "0" + h : h;
    let i: any = dd.getMinutes(); //分
    i = i < 10 ? "0" + i : i;
    let s: any = dd.getSeconds(); //秒
    s = s < 10 ? "0" + s : s;
    if (type == 1) {
      return y + "-" + m + "-" + d + " " + h + ":" + i + ":" + s;
    } else if (type == 2) {
      return y + "-" + m + "-" + d;
    } else if (type == 3) {
      return h + ":" + i + ":" + s;
    } else if (type == 4) {
      return m + "-" + d + " " + h + ":" + i + ":" + s;
    } else if (type == 5) {
      return y.toString() + m.toString() + d.toString();
    } else if (type == 6) {
      return y.toString() + m.toString() + d.toString() + h.toString() + i.toString() + s.toString();
    }
    return "";
  };
  /**
   * 格式化单位，超过10000 转换位 10K   10000K 转换为 10M
   * @param money 金钱数量
   * @returns String
   */
  public static formatMoney(money: number): string {
    const arrUnit = ["", "K", "M", "G", "T", "P", "E", "Z", "Y", "B", "N", "D"];

    let strValue = "";
    for (let idx = 0; idx < arrUnit.length; idx++) {
      if (money >= 10000) {
        money /= 1000;
      } else {
        strValue = Math.floor(money) + arrUnit[idx];
        break;
      }
    }

    if (strValue === "") {
      strValue = Math.floor(money) + "U"; //超过最大值就加个U
    }

    return strValue;
  }
  /**
   * 根据剩余秒数格式化剩余时间 返回格式 HH:MM:SS
   * @param  leftSec 剩余秒数
   * @returns String
   * @example 00:12:30
   */
  public static formatTimeForSecond(leftSec: number): string {
    let timeStr = "";
    const sec = leftSec % 60;

    let leftMin = Math.floor(leftSec / 60);
    leftMin = leftMin < 0 ? 0 : leftMin;

    const hour = Math.floor(leftMin / 60);
    const min = leftMin % 60;

    if (hour > 0) {
      timeStr += hour > 9 ? hour.toString() : "0" + hour;
      timeStr += ":";
    }

    timeStr += min > 9 ? min.toString() : "0" + min;
    timeStr += ":";
    timeStr += sec > 9 ? sec.toString() : "0" + sec;
    return timeStr;
  }
  /**
   *  根据剩余毫秒数格式化剩余时间 返回 HH:MM:SS
   * @param  ms 剩余毫秒数
   * @returns String
   * @example 00:12:30
   */
  public static formatTimeForMillisecond(ms: number): string {
    const second = Math.floor((ms / 1000) % 60);
    const minute = Math.floor((ms / 1000 / 60) % 60);
    const hour = Math.floor(ms / 1000 / 60 / 60);
    const strSecond = second < 10 ? "0" + second : second;
    const strMinute = minute < 10 ? "0" + minute : minute;
    const strHour = hour < 10 ? "0" + hour : hour;
    return `${strSecond}:${strMinute}:${strHour}`;
  }
  /****************************************************************
    /**
    * 数组中所有的文字链接
    * @param  arr 原数组
    * @returns String 
    */
  public static flattenDeep(arr: string[]) {
    return arr.reduce((prev: string, cur: string) => {
      return prev.concat(/*Array.isArray(cur) ? this.flattenDeep(cur) :*/ cur);
    });
  }
  /**
   * 模拟生成一个uuid
   * @returns 'e5d0b500-75f1-92f7-4588-cff500ccb5a7'
   */
  public static simulationUUID(): string {
    function s4() {
      return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
    }
    return s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4();
  }
  /**
   * 删除首位空的字符串
   * @param str 原字符串
   * @returns String
   */
  public static trim(str: string) {
    return str.replace(/(^\s*)|(\s*$)/g, "");
  }
  /**
   * 删除首位特殊字的字符串
   * @param str 原字符串
   * @returns String
   */
  public static trimD = function (str: string) {
    return str.replace(/(^"*)|("*$)/g, "");
  };
  /**
   * 获取汉字字符的长度
   * @param render 字符
   * @returns Number 长度
   */
  public static getStringLength(render: string) {
    const strArr = render;
    let len = 0;
    for (let i = 0, n = strArr.length; i < n; i++) {
      const val = strArr.charCodeAt(i);
      if (val <= 255) {
        len = len + 1;
      } else {
        len = len + 2;
      }
    }
    return Math.ceil(len / 2);
  }
  /**
   * md5加密
   *@param content 加密内容
   *@param type 大小写 true 为大写
   */
  public static md5(jia: "MD5" | "SHA1" | "SHA256", content: string, type?: boolean) {
    var md5 = crypto.createHash(jia);
    md5.update(content);
    return type ? md5.digest("hex").toUpperCase() : md5.digest("hex").toLowerCase();
  }
  /**随机一个字符串 */
  public static getRandomTxt = function (len) {
    let value = "";
    for (let i = 0; i < len; i++) {
      value += String(Math.floor(Math.random() * 10));
    }
    return value;
  };
}
